├── LICENSE ├── examples ├── rich.html ├── twitter2.html ├── embed.html ├── photos.html ├── videos.html ├── twitter.html ├── youtube.html └── sample.html ├── qunit.css ├── README.rst ├── tests.html ├── jquery.embedly.min.js ├── jquery.embedly.js └── qunit.js /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Embed.ly, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of Embed.ly nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /examples/rich.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Embedly - Embed - Example 5 | 6 | 7 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 28 | 29 |
30 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /examples/twitter2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Embedly - Embed - Example 6 | 7 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 37 | 38 |
39 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /examples/embed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Embedly - Embed - Example 5 | 6 | 7 | 18 | 19 | 20 | 21 | 22 |
23 | 24 |
25 | 26 | 27 |
28 | 29 |
30 |

Enter a url from one of Embedly's supported services.

31 |
32 | 33 |
34 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /examples/photos.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Embedly - Embed - Example 5 | 6 | 7 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 35 | 36 |
37 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /examples/videos.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Embedly - Embed - Example 5 | 6 | 7 | 18 | 19 | 20 | 21 |
22 | 23 | 33 | 34 |
35 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /examples/twitter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Embedly - Embed - Example 6 | 7 | 8 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 |
28 | 29 | 30 |
31 | 32 | 34 | 35 |
36 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /examples/youtube.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Embedly - Embed - Example 5 | 6 | 7 | 26 | 27 | 28 |
29 | 30 |
31 |
32 |

My favorite Memes

33 | 39 |
40 |
41 |
42 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /examples/sample.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Embedly - Embed - Example 5 | 6 | 7 | 25 | 26 | 27 |
28 | 29 |
30 |
31 |

My favorite Memes

32 | 44 |
45 |
46 |
47 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /qunit.css: -------------------------------------------------------------------------------- 1 | /** Font Family and Sizes */ 2 | 3 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 4 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; 5 | } 6 | 7 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 8 | #qunit-tests { font-size: smaller; } 9 | 10 | 11 | /** Resets */ 12 | 13 | #qunit-tests, #qunit-tests li ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | 19 | /** Header */ 20 | 21 | #qunit-header { 22 | padding: 0.5em 0 0.5em 1em; 23 | 24 | color: #fff; 25 | text-shadow: rgba(0, 0, 0, 0.5) 4px 4px 1px; 26 | background-color: #0d3349; 27 | 28 | border-radius: 15px 15px 0 0; 29 | -moz-border-radius: 15px 15px 0 0; 30 | -webkit-border-top-right-radius: 15px; 31 | -webkit-border-top-left-radius: 15px; 32 | } 33 | 34 | #qunit-header a { 35 | text-decoration: none; 36 | color: white; 37 | } 38 | 39 | #qunit-banner { 40 | height: 5px; 41 | } 42 | 43 | #qunit-testrunner-toolbar { 44 | padding: 0em 0 0.5em 2em; 45 | } 46 | 47 | #qunit-userAgent { 48 | padding: 0.5em 0 0.5em 2.5em; 49 | background-color: #2b81af; 50 | color: #fff; 51 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 52 | } 53 | 54 | 55 | /** Tests: Pass/Fail */ 56 | 57 | #qunit-tests { 58 | list-style-position: inside; 59 | } 60 | 61 | #qunit-tests li { 62 | padding: 0.4em 0.5em 0.4em 2.5em; 63 | border-bottom: 1px solid #fff; 64 | list-style-position: inside; 65 | } 66 | 67 | #qunit-tests li strong { 68 | cursor: pointer; 69 | } 70 | 71 | #qunit-tests li ol { 72 | margin-top: 0.5em; 73 | padding: 0.5em; 74 | 75 | background-color: #fff; 76 | 77 | border-radius: 15px; 78 | -moz-border-radius: 15px; 79 | -webkit-border-radius: 15px; 80 | 81 | box-shadow: inset 0px 2px 13px #999; 82 | -moz-box-shadow: inset 0px 2px 13px #999; 83 | -webkit-box-shadow: inset 0px 2px 13px #999; 84 | } 85 | 86 | #qunit-tests li li { 87 | margin: 0.5em; 88 | padding: 0.4em 0.5em 0.4em 0.5em; 89 | background-color: #fff; 90 | border-bottom: none; 91 | list-style-position: inside; 92 | } 93 | 94 | /*** Passing Styles */ 95 | 96 | #qunit-tests li li.pass { 97 | color: #5E740B; 98 | background-color: #fff; 99 | border-left: 26px solid #C6E746; 100 | } 101 | 102 | #qunit-tests li.pass { color: #528CE0; background-color: #D2E0E6; } 103 | #qunit-tests li.pass span.test-name { color: #366097; } 104 | 105 | #qunit-tests li li.pass span.test-actual, 106 | #qunit-tests li li.pass span.test-expected { color: #999999; } 107 | 108 | strong b.pass { color: #5E740B; } 109 | 110 | #qunit-banner.qunit-pass { background-color: #C6E746; } 111 | 112 | /*** Failing Styles */ 113 | 114 | #qunit-tests li li.fail { 115 | color: #710909; 116 | background-color: #fff; 117 | border-left: 26px solid #EE5757; 118 | } 119 | 120 | #qunit-tests li.fail { color: #000000; background-color: #EE5757; } 121 | #qunit-tests li.fail span.test-name, 122 | #qunit-tests li.fail span.module-name { color: #000000; } 123 | 124 | #qunit-tests li li.fail span.test-actual { color: #EE5757; } 125 | #qunit-tests li li.fail span.test-expected { color: green; } 126 | 127 | strong b.fail { color: #710909; } 128 | 129 | #qunit-banner.qunit-fail, 130 | #qunit-testrunner-toolbar { background-color: #EE5757; } 131 | 132 | 133 | /** Footer */ 134 | 135 | #qunit-testresult { 136 | padding: 0.5em 0.5em 0.5em 2.5em; 137 | 138 | color: #2b81af; 139 | background-color: #D2E0E6; 140 | 141 | border-radius: 0 0 15px 15px; 142 | -moz-border-radius: 0 0 15px 15px; 143 | -webkit-border-bottom-right-radius: 15px; 144 | -webkit-border-bottom-left-radius: 15px; 145 | } 146 | 147 | /** Fixture */ 148 | 149 | #qunit-fixture { 150 | position: absolute; 151 | top: -10000px; 152 | left: -10000px; 153 | } 154 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ================================================================== 2 | Embedly - JQuery - An OEmbed Library to Replace Links with Content 3 | ================================================================== 4 | 5 | Embedly - JQuery is a JQuery Library for Embedly that will replace links with 6 | content. It follows the oEmbed spec (http://oembed.com) for content retrieval, 7 | while utilizing http://api.embed.ly as a single endpoint. 8 | 9 | Documentation 10 | ============= 11 | 12 | The most up-to-date documentation can be found on the `README 13 | `_. If you've discovered the recursive nature of that statement, good on you. 14 | You can continue to read the most up-to-date documentation below. 15 | 16 | Updates in Rev 2.0.0 17 | ==================== 18 | * added support for jQuery chainability 19 | * new usage of jQuery.data() for saving oEmbed data to the DOM 20 | * added a custom event for handling oEmbed returns 21 | * added support for pro.embed.ly 22 | * added qUnit testing suite and tests 23 | 24 | Requirements 25 | ============ 26 | 27 | Requires JQuery 1.3.1 or greater:: 28 | 29 | 30 | 31 | 32 | Using Embedly-JQuery 33 | ==================== 34 | 35 | There are two ways to interact with this library. You can call Embedly directly 36 | or use CSS Selectors to replace links 37 | 38 | Call Directly 39 | ------------- 40 | Will Return a JSON object representing an oembed or null, and a dictionary object representing the original url and DOM node. 41 | :: 42 | 43 | #Alert the tile of a video 44 | $.embedly('http://www.youtube.com/watch?v=LfamTmY5REw', {}, function(oembed, dict){ 45 | alert(oembed.title); 46 | }); 47 | 48 | # Call with maxWidth option set to 600px and maxHeight option set to 400px 49 | $.embedly('http://www.youtube.com/watch?v=LfamTmY5REw', 50 | { maxWidth: 600, 51 | maxHeight: 400, 52 | success: function(oembed, dict){ 53 | alert(oembed.title); 54 | }); 55 | 56 | # Pass in an array of urls to load simultaneously 57 | $.embedly(['http://www.youtube.com/watch?v=LfamTmY5REw', 58 | 'http://www.youtube.com/watch?v=lOC_JjNFkVw', 59 | 'http://www.youtube.com/watch?v=cTl3U6aSd2w'], 60 | {maxWidth: 600, 61 | success: function(oembed, dict){ 62 | alert(oembed.title); 63 | }); 64 | # Include a jQuery set of DOM elements so it behaves more like the CSS Selector form below 65 | # this form returns the jQuery set, so you can chain jQuery functions after it 66 | $.embedly('http://www.youtube.com/watch?v=LfamTmY5REw', 67 | {maxWidth: 600, 68 | elems: $('#element'), 69 | success: function(oembed, dict){ 70 | alert(oembed.title); 71 | }); 72 | 73 | CSS Selector 74 | ------------ 75 | Use a CSS selector to replace every valid link with an embed on the page. 76 | :: 77 | 78 | # Replace all valid links 79 | $('a').embedly(); 80 | 81 | # Replace a subset of links 82 | $('a.oembed').embedly(); 83 | 84 | # Replace with maxWidth option set to 600px and method option set to 'after' 85 | $('a').embedly({maxWidth:600,'method':'after'}); 86 | 87 | # Replace only Hulu links 88 | $('a').embedly({maxWidth:600,'urlRe': /http:\/\/(www\.hulu\.com\/watch.*)/i,'method':'after'}); 89 | 90 | # Embedly now supports chaining, so you can modify your original jQuery set after triggering Embedly 91 | $('a).embedly({maxWidth:450}).css('backgroundColor','#dadada'); 92 | 93 | Valid Options 94 | ------------- 95 | 96 | maxWidth [`Number:null`] 97 | A number representing the "max width" in pixels a piece of content can be displayed in your page. 98 | 99 | maxHeight [`Number:null`] 100 | A number representing the "max height" in pixels a piece of content can be displayed in your page. 101 | 102 | urlRe [`RegEx:`] 103 | A regular expression representing what links to show content for. 104 | Use : http://api.embed.ly/tools/generator to generate regular expressions for a specific set of sources. 105 | 106 | method [`String:'replace'`] 107 | A string value either "replace" or "after" to tell Embedly how to place the content in your page. 108 | 109 | * `replace` - replaces the link with the content. 110 | * `after` - inserts the content after the link. 111 | * `afterParent` - inserts the content after the parent element. 112 | 113 | wrapElement [`String:'div'`] 114 | A string value representing the valid html element to wrap the content in. 115 | 116 | className [`String:'embed'`] 117 | A string value representing a CSS class you would like to assign to the wrapElement. 118 | 119 | addImageStyles [`Boolean:true`] 120 | A boolean value representing whether or not Embedly should use the style element to resize images based on the maxWidth and maxHeight parameters 121 | 122 | embedly_wmode [`String:'opaque'`] 123 | A string value either `window`, `opaque` or `transparent` representing the flash WMODE parameter which allows layering of Flash content with DHTML layers. 124 | 125 | * `window` - movie plays in its own rectangular window on a web page. 126 | * `opaque` - the movie hides everything on the page behind it. 127 | * `transparent` - the background of the HTML page shows through all transparent portions of the movie, this may slow animation performance. 128 | key [`string:''`] 129 | If you are an Embedly Pro user, you should have a Pro Key assigned. Log in at `http://pro.embed.ly/login `_ to find your API Key. This will unlock all the features of 130 | pro.embed.ly for your site 131 | 132 | success [`Function:default function`] 133 | If you would like to replace our default callback action, which takes ['replace','after','afterParent'] as a parameter and writes the oEmbed.code to your DOM element, 134 | You may do so with this function. Alternatively you can use the optional function parameter in the embedly({}, function(){}) call, but we're deprecating that in favor 135 | of this optional parameter. If you want to access the oEmbed data, but still keep the default callback function, we have introduced a new custom event handler that fires 136 | when the oEmbed object is returned. Read below for more information on that 137 | 138 | Pro 139 | === 140 | Starting in revision 2.0.0 we are supporting Embedly Pro. While API requests are limited to the 150+ services we currently support and maintain, Pro users get the added 141 | benefit of being able to preview any URL. We will return a title, a description, and any relevant information we can scrape from the URL. If you would like to learn more, 142 | visit `http://pro.embed.ly `_ If you have your Pro Key, be sure to include it in your optional parameters. 143 | 144 | Custom Event 145 | ============ 146 | Starting in revision 2.0.0 we have started writing the oEmbed data to the DOM elements using jQuery.data(). You can read more about the data function `here `_, but basically 147 | saves the oembed data on the element for retrieval later. For example: 148 | :: 149 | 150 | # $('a').embedly() 151 | # ... after the AJAX returns an oembed ... 152 | $('a').data('oembed') 153 | 154 | This call returns the oembed object for each a tag, so you can access the data later on. Because this data is not written to the DOM until the AJAX requests are complete we have added a 155 | custom event listener called 'embedly-oembed.' This event fires for each node when the oEmbed object is written to the node using jQuery.data(). We did this so that developers could 156 | continue to use our default callback function for writing embeds to the page and still have access to the oembed data for customization. 157 | :: 158 | 159 | # version 1 160 | $('a').embedly({maxWidth:500}).bind('embedly-oembed', function(e){ 161 | var oembed = $(this).data('oembed'); 162 | alert(oembed.title); 163 | }); 164 | 165 | # version 2 166 | $('a').embedly({maxWidth:500}).bind('embedly-oembed', function(e, oembed){ 167 | alert(oembed.title); 168 | }); 169 | 170 | The event handler gets the oembed object passed in as a parameter as well if you don't want to use jQuery.data(); The two are equivalent. 171 | 172 | Examples 173 | ----------------------------------------- 174 | Examples can be found at - http://github.com/embedly/embedly-jquery/tree/master/examples/ 175 | 176 | Licensing 177 | --------- 178 | BSD License can be found at - http://github.com/embedly/embedly-jquery/tree/master/LICENSE/ 179 | 180 | Embedly URLs 181 | ------------ 182 | 183 | * Git location: http://github.com/embedly/embedly-jquery/ 184 | * Home Page: http://embed.ly 185 | * API Page: http://api.embed.ly 186 | * Support: http://support.embed.ly 187 | 188 | -------------------------------------------------------------------------------- /tests.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 75 | 76 | 77 |

QUnit example

78 |

79 |

80 |
    81 |
82 |

Test Urls

83 | 142 | 143 | -------------------------------------------------------------------------------- /jquery.embedly.min.js: -------------------------------------------------------------------------------- 1 | (function(a){window.embedlyURLre=/http:\/\/(.*youtube\.com\/watch.*|.*\.youtube\.com\/v\/.*|youtu\.be\/.*|.*\.youtube\.com\/user\/.*|.*\.youtube\.com\/.*#.*\/.*|m\.youtube\.com\/watch.*|m\.youtube\.com\/index.*|.*\.youtube\.com\/profile.*|.*justin\.tv\/.*|.*justin\.tv\/.*\/b\/.*|.*justin\.tv\/.*\/w\/.*|www\.ustream\.tv\/recorded\/.*|www\.ustream\.tv\/channel\/.*|www\.ustream\.tv\/.*|qik\.com\/video\/.*|qik\.com\/.*|qik\.ly\/.*|.*revision3\.com\/.*|.*\.dailymotion\.com\/video\/.*|.*\.dailymotion\.com\/.*\/video\/.*|www\.collegehumor\.com\/video:.*|.*twitvid\.com\/.*|www\.break\.com\/.*\/.*|vids\.myspace\.com\/index\.cfm\?fuseaction=vids\.individual&videoid.*|www\.myspace\.com\/index\.cfm\?fuseaction=.*&videoid.*|www\.metacafe\.com\/watch\/.*|www\.metacafe\.com\/w\/.*|blip\.tv\/file\/.*|.*\.blip\.tv\/file\/.*|video\.google\.com\/videoplay\?.*|.*revver\.com\/video\/.*|video\.yahoo\.com\/watch\/.*\/.*|video\.yahoo\.com\/network\/.*|.*viddler\.com\/explore\/.*\/videos\/.*|liveleak\.com\/view\?.*|www\.liveleak\.com\/view\?.*|animoto\.com\/play\/.*|dotsub\.com\/view\/.*|www\.overstream\.net\/view\.php\?oid=.*|www\.livestream\.com\/.*|www\.worldstarhiphop\.com\/videos\/video.*\.php\?v=.*|worldstarhiphop\.com\/videos\/video.*\.php\?v=.*|teachertube\.com\/viewVideo\.php.*|www\.teachertube\.com\/viewVideo\.php.*|www1\.teachertube\.com\/viewVideo\.php.*|www2\.teachertube\.com\/viewVideo\.php.*|bambuser\.com\/v\/.*|bambuser\.com\/channel\/.*|bambuser\.com\/channel\/.*\/broadcast\/.*|www\.schooltube\.com\/video\/.*\/.*|bigthink\.com\/ideas\/.*|bigthink\.com\/series\/.*|sendables\.jibjab\.com\/view\/.*|sendables\.jibjab\.com\/originals\/.*|www\.xtranormal\.com\/watch\/.*|.*yfrog\..*\/.*|tweetphoto\.com\/.*|www\.flickr\.com\/photos\/.*|flic\.kr\/.*|.*twitpic\.com\/.*|.*imgur\.com\/.*|.*\.posterous\.com\/.*|post\.ly\/.*|twitgoo\.com\/.*|i.*\.photobucket\.com\/albums\/.*|s.*\.photobucket\.com\/albums\/.*|phodroid\.com\/.*\/.*\/.*|www\.mobypicture\.com\/user\/.*\/view\/.*|moby\.to\/.*|xkcd\.com\/.*|www\.xkcd\.com\/.*|imgs\.xkcd\.com\/.*|www\.asofterworld\.com\/index\.php\?id=.*|www\.asofterworld\.com\/.*\.jpg|asofterworld\.com\/.*\.jpg|www\.qwantz\.com\/index\.php\?comic=.*|23hq\.com\/.*\/photo\/.*|www\.23hq\.com\/.*\/photo\/.*|.*dribbble\.com\/shots\/.*|drbl\.in\/.*|.*\.smugmug\.com\/.*|.*\.smugmug\.com\/.*#.*|emberapp\.com\/.*\/images\/.*|emberapp\.com\/.*\/images\/.*\/sizes\/.*|emberapp\.com\/.*\/collections\/.*\/.*|emberapp\.com\/.*\/categories\/.*\/.*\/.*|embr\.it\/.*|picasaweb\.google\.com.*\/.*\/.*#.*|picasaweb\.google\.com.*\/lh\/photo\/.*|picasaweb\.google\.com.*\/.*\/.*|dailybooth\.com\/.*\/.*|brizzly\.com\/pic\/.*|pics\.brizzly\.com\/.*\.jpg|img\.ly\/.*|www\.tinypic\.com\/view\.php.*|tinypic\.com\/view\.php.*|www\.tinypic\.com\/player\.php.*|tinypic\.com\/player\.php.*|www\.tinypic\.com\/r\/.*\/.*|tinypic\.com\/r\/.*\/.*|.*\.tinypic\.com\/.*\.jpg|.*\.tinypic\.com\/.*\.png|meadd\.com\/.*\/.*|meadd\.com\/.*|.*\.deviantart\.com\/art\/.*|.*\.deviantart\.com\/gallery\/.*|.*\.deviantart\.com\/#\/.*|fav\.me\/.*|.*\.deviantart\.com|.*\.deviantart\.com\/gallery|.*\.deviantart\.com\/.*\/.*\.jpg|.*\.deviantart\.com\/.*\/.*\.gif|.*\.deviantart\.net\/.*\/.*\.jpg|.*\.deviantart\.net\/.*\/.*\.gif|plixi\.com\/p\/.*|plixi\.com\/profile\/home\/.*|plixi\.com\/.*|www\.fotopedia\.com\/.*\/.*|fotopedia\.com\/.*\/.*|photozou\.jp\/photo\/show\/.*\/.*|photozou\.jp\/photo\/photo_only\/.*\/.*|instagr\.am\/p\/.*|skitch\.com\/.*\/.*\/.*|img\.skitch\.com\/.*|share\.ovi\.com\/media\/.*\/.*|www\.questionablecontent\.net\/|questionablecontent\.net\/|www\.questionablecontent\.net\/view\.php.*|questionablecontent\.net\/view\.php.*|questionablecontent\.net\/comics\/.*\.png|www\.questionablecontent\.net\/comics\/.*\.png|picplz\.com\/user\/.*\/pic\/.*\/|twitrpix\.com\/.*|.*\.twitrpix\.com\/.*|www\.someecards\.com\/.*\/.*|someecards\.com\/.*\/.*|some\.ly\/.*|www\.some\.ly\/.*|www\.whitehouse\.gov\/photos-and-video\/video\/.*|www\.whitehouse\.gov\/video\/.*|wh\.gov\/photos-and-video\/video\/.*|wh\.gov\/video\/.*|www\.hulu\.com\/watch.*|www\.hulu\.com\/w\/.*|hulu\.com\/watch.*|hulu\.com\/w\/.*|.*crackle\.com\/c\/.*|www\.fancast\.com\/.*\/videos|www\.funnyordie\.com\/videos\/.*|www\.funnyordie\.com\/m\/.*|funnyordie\.com\/videos\/.*|funnyordie\.com\/m\/.*|www\.vimeo\.com\/groups\/.*\/videos\/.*|www\.vimeo\.com\/.*|vimeo\.com\/m\/#\/featured\/.*|vimeo\.com\/groups\/.*\/videos\/.*|vimeo\.com\/.*|vimeo\.com\/m\/#\/featured\/.*|www\.ted\.com\/talks\/.*\.html.*|www\.ted\.com\/talks\/lang\/.*\/.*\.html.*|www\.ted\.com\/index\.php\/talks\/.*\.html.*|www\.ted\.com\/index\.php\/talks\/lang\/.*\/.*\.html.*|.*nfb\.ca\/film\/.*|www\.thedailyshow\.com\/watch\/.*|www\.thedailyshow\.com\/full-episodes\/.*|www\.thedailyshow\.com\/collection\/.*\/.*\/.*|movies\.yahoo\.com\/movie\/.*\/video\/.*|movies\.yahoo\.com\/movie\/.*\/trailer|movies\.yahoo\.com\/movie\/.*\/video|www\.colbertnation\.com\/the-colbert-report-collections\/.*|www\.colbertnation\.com\/full-episodes\/.*|www\.colbertnation\.com\/the-colbert-report-videos\/.*|www\.comedycentral\.com\/videos\/index\.jhtml\?.*|www\.theonion\.com\/video\/.*|theonion\.com\/video\/.*|wordpress\.tv\/.*\/.*\/.*\/.*\/|www\.traileraddict\.com\/trailer\/.*|www\.traileraddict\.com\/clip\/.*|www\.traileraddict\.com\/poster\/.*|www\.escapistmagazine\.com\/videos\/.*|www\.trailerspy\.com\/trailer\/.*\/.*|www\.trailerspy\.com\/trailer\/.*|www\.trailerspy\.com\/view_video\.php.*|www\.atom\.com\/.*\/.*\/|fora\.tv\/.*\/.*\/.*\/.*|www\.spike\.com\/video\/.*|www\.gametrailers\.com\/video\/.*|gametrailers\.com\/video\/.*|www\.koldcast\.tv\/video\/.*|www\.koldcast\.tv\/#video:.*|techcrunch\.tv\/watch.*|techcrunch\.tv\/.*\/watch.*|mixergy\.com\/.*|video\.pbs\.org\/video\/.*|www\.zapiks\.com\/.*|tv\.digg\.com\/.*|www\.trutv\.com\/video\/.*|www\.nzonscreen\.com\/title\/.*|nzonscreen\.com\/title\/.*|www\.godtube\.com\/featured\/video\/.*|godtube\.com\/featured\/video\/.*|www\.godtube\.com\/watch\/.*|godtube\.com\/watch\/.*|www\.tangle\.com\/view_video.*|mediamatters\.org\/mmtv\/.*|www\.clikthrough\.com\/theater\/video\/.*|soundcloud\.com\/.*|soundcloud\.com\/.*\/.*|soundcloud\.com\/.*\/sets\/.*|soundcloud\.com\/groups\/.*|www\.last\.fm\/music\/.*|www\.last\.fm\/music\/+videos\/.*|www\.last\.fm\/music\/+images\/.*|www\.last\.fm\/music\/.*\/_\/.*|www\.last\.fm\/music\/.*\/.*|www\.mixcloud\.com\/.*\/.*\/|www\.radionomy\.com\/.*\/radio\/.*|radionomy\.com\/.*\/radio\/.*|www\.entertonement\.com\/clips\/.*|www\.rdio\.com\/#\/artist\/.*\/album\/.*|www\.rdio\.com\/artist\/.*\/album\/.*|www\.zero-inch\.com\/.*|.*\.bandcamp\.com\/|.*\.bandcamp\.com\/track\/.*|.*\.bandcamp\.com\/album\/.*|freemusicarchive\.org\/music\/.*|www\.freemusicarchive\.org\/music\/.*|freemusicarchive\.org\/curator\/.*|www\.freemusicarchive\.org\/curator\/.*|www\.npr\.org\/.*\/.*\/.*\/.*\/.*|www\.npr\.org\/.*\/.*\/.*\/.*\/.*\/.*|www\.npr\.org\/.*\/.*\/.*\/.*\/.*\/.*\/.*|www\.npr\.org\/templates\/story\/story\.php.*|espn\.go\.com\/video\/clip.*|espn\.go\.com\/.*\/story.*|abcnews\.com\/.*\/video\/.*|abcnews\.com\/video\/playerIndex.*|washingtonpost\.com\/wp-dyn\/.*\/video\/.*\/.*\/.*\/.*|www\.washingtonpost\.com\/wp-dyn\/.*\/video\/.*\/.*\/.*\/.*|www\.boston\.com\/video.*|boston\.com\/video.*|www\.facebook\.com\/photo\.php.*|www\.facebook\.com\/video\/video\.php.*|www\.facebook\.com\/v\/.*|cnbc\.com\/id\/.*\?.*video.*|www\.cnbc\.com\/id\/.*\?.*video.*|cnbc\.com\/id\/.*\/play\/1\/video\/.*|www\.cnbc\.com\/id\/.*\/play\/1\/video\/.*|cbsnews\.com\/video\/watch\/.*|www\.google\.com\/buzz\/.*\/.*\/.*|www\.google\.com\/buzz\/.*|www\.google\.com\/profiles\/.*|google\.com\/buzz\/.*\/.*\/.*|google\.com\/buzz\/.*|google\.com\/profiles\/.*|www\.cnn\.com\/video\/.*|edition\.cnn\.com\/video\/.*|money\.cnn\.com\/video\/.*|today\.msnbc\.msn\.com\/id\/.*\/vp\/.*|www\.msnbc\.msn\.com\/id\/.*\/vp\/.*|www\.msnbc\.msn\.com\/id\/.*\/ns\/.*|today\.msnbc\.msn\.com\/id\/.*\/ns\/.*|multimedia\.foxsports\.com\/m\/video\/.*\/.*|msn\.foxsports\.com\/video.*|www\.globalpost\.com\/video\/.*|www\.globalpost\.com\/dispatch\/.*|.*amazon\..*\/gp\/product\/.*|.*amazon\..*\/.*\/dp\/.*|.*amazon\..*\/dp\/.*|.*amazon\..*\/o\/ASIN\/.*|.*amazon\..*\/gp\/offer-listing\/.*|.*amazon\..*\/.*\/ASIN\/.*|.*amazon\..*\/gp\/product\/images\/.*|www\.amzn\.com\/.*|amzn\.com\/.*|www\.shopstyle\.com\/browse.*|www\.shopstyle\.com\/action\/apiVisitRetailer.*|www\.shopstyle\.com\/action\/viewLook.*|gist\.github\.com\/.*|twitter\.com\/.*\/status\/.*|twitter\.com\/.*\/statuses\/.*|mobile\.twitter\.com\/.*\/status\/.*|mobile\.twitter\.com\/.*\/statuses\/.*|www\.crunchbase\.com\/.*\/.*|crunchbase\.com\/.*\/.*|www\.slideshare\.net\/.*\/.*|www\.slideshare\.net\/mobile\/.*\/.*|.*\.scribd\.com\/doc\/.*|screenr\.com\/.*|polldaddy\.com\/community\/poll\/.*|polldaddy\.com\/poll\/.*|answers\.polldaddy\.com\/poll\/.*|www\.5min\.com\/Video\/.*|www\.howcast\.com\/videos\/.*|www\.screencast\.com\/.*\/media\/.*|screencast\.com\/.*\/media\/.*|www\.screencast\.com\/t\/.*|screencast\.com\/t\/.*|issuu\.com\/.*\/docs\/.*|www\.kickstarter\.com\/projects\/.*\/.*|www\.scrapblog\.com\/viewer\/viewer\.aspx.*|ping\.fm\/p\/.*|chart\.ly\/.*|maps\.google\.com\/maps\?.*|maps\.google\.com\/\?.*|maps\.google\.com\/maps\/ms\?.*|.*\.craigslist\.org\/.*\/.*|my\.opera\.com\/.*\/albums\/show\.dml\?id=.*|my\.opera\.com\/.*\/albums\/showpic\.dml\?album=.*&picture=.*|tumblr\.com\/.*|.*\.tumblr\.com\/post\/.*|www\.polleverywhere\.com\/polls\/.*|www\.polleverywhere\.com\/multiple_choice_polls\/.*|www\.polleverywhere\.com\/free_text_polls\/.*|www\.quantcast\.com\/wd:.*|www\.quantcast\.com\/.*|siteanalytics\.compete\.com\/.*|statsheet\.com\/statplot\/charts\/.*\/.*\/.*\/.*|statsheet\.com\/statplot\/charts\/e\/.*|statsheet\.com\/.*\/teams\/.*\/.*|statsheet\.com\/tools\/chartlets\?chart=.*|.*\.status\.net\/notice\/.*|identi\.ca\/notice\/.*|brainbird\.net\/notice\/.*|shitmydadsays\.com\/notice\/.*|www\.studivz\.net\/Profile\/.*|www\.studivz\.net\/l\/.*|www\.studivz\.net\/Groups\/Overview\/.*|www\.studivz\.net\/Gadgets\/Info\/.*|www\.studivz\.net\/Gadgets\/Install\/.*|www\.studivz\.net\/.*|www\.meinvz\.net\/Profile\/.*|www\.meinvz\.net\/l\/.*|www\.meinvz\.net\/Groups\/Overview\/.*|www\.meinvz\.net\/Gadgets\/Info\/.*|www\.meinvz\.net\/Gadgets\/Install\/.*|www\.meinvz\.net\/.*|www\.schuelervz\.net\/Profile\/.*|www\.schuelervz\.net\/l\/.*|www\.schuelervz\.net\/Groups\/Overview\/.*|www\.schuelervz\.net\/Gadgets\/Info\/.*|www\.schuelervz\.net\/Gadgets\/Install\/.*|www\.schuelervz\.net\/.*|myloc\.me\/.*|pastebin\.com\/.*|pastie\.org\/.*|www\.pastie\.org\/.*|redux\.com\/stream\/item\/.*\/.*|redux\.com\/f\/.*\/.*|www\.redux\.com\/stream\/item\/.*\/.*|www\.redux\.com\/f\/.*\/.*|cl\.ly\/.*|cl\.ly\/.*\/content|speakerdeck\.com\/u\/.*\/p\/.*|www\.kiva\.org\/lend\/.*|www\.timetoast\.com\/timelines\/.*|storify\.com\/.*\/.*|.*meetup\.com\/.*|meetu\.ps\/.*|www\.dailymile\.com\/people\/.*\/entries\/.*|.*\.kinomap\.com\/.*|www\.metacdn\.com\/api\/users\/.*\/content\/.*|www\.metacdn\.com\/api\/users\/.*\/media\/.*|prezi\.com\/.*\/.*)/i;a.embedly=a.embedly||{};if(a.embedly.version){return}a.embedly.version="2.0.0";a.extend({embedly:function(j,o,m){var b=[];var d={maxWidth:null,maxHeight:null,wmode:"opaque",method:"replace",addImageStyles:true,wrapElement:"div",className:"embed",urlRe:null,key:null,elems:[],success:null};var c;if(typeof o!="undefined"){c=a.extend(d,o)}else{c=d}if(c.urlRe==null){c.urlRe=window.embedlyURLre}if(typeof j=="string"){j=new Array(j)}if(typeof m!="undefined"){c.success=m}if(c.success==null){c.success=function(p,s){var q,r=a(s.node);if(!(p)){return null}if((q=c.method)==="replace"){return r.replaceWith(p.code)}else{if(q==="after"){return r.after(p.code)}else{if(q==="afterParent"){return r.parent().after(p.code)}}}}}var l=function(p){return(p.match(c.urlRe)!==null)};var n=function(q){var p="urls="+q;if(c.maxWidth!=null){p+="&maxwidth="+c.maxWidth}else{if(typeof dimensions!="undefined"){p+="&maxwidth="+dimensions.width}}if(c.maxHeight!=null){p+="&maxheight="+c.maxHeight}p+="&wmode="+c.wmode;if(typeof c.key=="string"){p+="&key="+c.key}return p};var h=function(){return typeof c.key=="string"?"http://pro.embed.ly/1/oembed":"http://api.embed.ly/1/oembed"};var f=function(p,u){var q,s,r,t;if((q=p.type)==="photo"){t=p.title||"";r=[];if(c.addImageStyles){if(c.maxWidth){units=typeof c.maxHeight=="number"?"px":"";r.push("max-width: "+(c.maxWidth)+units)}if(c.maxHeight){units=typeof c.maxHeight=="number"?"px":"";r.push("max-height: "+(c.maxHeight)+units)}}r=r.join(";");s=""+t+""}else{if(q==="video"){s=p.html}else{if(q==="rich"){s=p.html}else{t=p.title||u.url;thumb=p.thumbnail_url?'':"";description=p.description;provider=p.provider_name?''+p.provider_name+" - ":"";s=thumb+""+t+"";s+=provider;s+=description}}}if(c.wrapElement){s="<"+c.wrapElement+' class="'+c.className+'">'+s+""}p.code=s;if(typeof u.node!="undefined"){a(u.node).data("oembed",p).trigger("embedly-oembed",[p])}return c.success(p,u)};var e=function(p){var r,t,s,q;s=a.map(p,function(v,u){if(u==0){if(v.node!==null){node=a(v.node);q={width:node.parent().width(),height:node.parent().height()}}}return encodeURIComponent(v.url)}).join(",");a.ajax({url:h(),dataType:"jsonp",data:n(s),success:function(u){return a.each(u,function(v,w){return w.type!="error"?f(w,p[v]):null})}})};a.each(j,function(q,p){node=typeof c.elems!=="undefined"?c.elems[q]:null;if(typeof node!="undefined"&&!(l(p)||c.key)){a(node).data("oembed",false)}return p&&(l(p)||c.key)?b.push({url:p,node:node}):null});var k=[];var g=b.length;for(i=0;(0<=g?ig);i+=20){k=k.concat(e(b.slice(i,i+20)))}if(c.elems){return c.elems}else{return this}}});a.fn.embedly=function(e,h){var b=window.embedlyURLre;var f=typeof e!="undefined"?e:{};if(typeof h!="undefined"){e.success=h}var g=new Array();var d=new Array();this.each(function(){if(typeof a(this).attr("href")!="undefined"){if(b.test(a(this).attr("href"))){g.push(a(this).attr("href"));d.push(a(this))}}else{a(this).find("a").each(function(){if(typeof a(this).attr("href")!="undefined"&&b.test(a(this).attr("href"))){g.push(a(this).attr("href"));d.push(a(this))}})}f.elems=d});var c=a.embedly(g,f);return this}})(jQuery); -------------------------------------------------------------------------------- /jquery.embedly.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Embedly JQuery v2.0.0 3 | * ============== 4 | * This library allows you to easily embed objects on any page. 5 | * 6 | * Requirements: 7 | * ------------- 8 | * jquery-1.3 or higher 9 | * 10 | * Usage: 11 | * ------ 12 | * There are two ways to interact with this lib. One exposes a simple method to call embedly directly 13 | * 14 | * >>> $.embedly('http://www.youtube.com/watch?v=LfamTmY5REw', {}, function(oembed){ alert(oembed.title);}); 15 | * 16 | * The oembed is either a json object or null 17 | * 18 | * You can also reference it this way, which will try and replace every link on the page with an embed 19 | * 20 | * Documentation is availiable at http://github.com/embedly/embedly-jquery 21 | * 22 | * $('a').embedly(); 23 | * 24 | * The Options Are as Follows 25 | * 26 | * maxWidth : null, 27 | * maxHeight: null, 28 | * urlRe : null, 29 | * method : 'replace', 30 | * wrapElement : 'div', 31 | * className : 'embed', 32 | * addImageStyles : true, 33 | * wmode : null} //after 34 | * 35 | * http://api.embed.ly/tools/generator - generate your own regex for only sources you want 36 | * 37 | */ 38 | 39 | (function($){ 40 | window.embedlyURLre = /http:\/\/(.*youtube\.com\/watch.*|.*\.youtube\.com\/v\/.*|youtu\.be\/.*|.*\.youtube\.com\/user\/.*|.*\.youtube\.com\/.*#.*\/.*|m\.youtube\.com\/watch.*|m\.youtube\.com\/index.*|.*\.youtube\.com\/profile.*|.*justin\.tv\/.*|.*justin\.tv\/.*\/b\/.*|.*justin\.tv\/.*\/w\/.*|www\.ustream\.tv\/recorded\/.*|www\.ustream\.tv\/channel\/.*|www\.ustream\.tv\/.*|qik\.com\/video\/.*|qik\.com\/.*|qik\.ly\/.*|.*revision3\.com\/.*|.*\.dailymotion\.com\/video\/.*|.*\.dailymotion\.com\/.*\/video\/.*|www\.collegehumor\.com\/video:.*|.*twitvid\.com\/.*|www\.break\.com\/.*\/.*|vids\.myspace\.com\/index\.cfm\?fuseaction=vids\.individual&videoid.*|www\.myspace\.com\/index\.cfm\?fuseaction=.*&videoid.*|www\.metacafe\.com\/watch\/.*|www\.metacafe\.com\/w\/.*|blip\.tv\/file\/.*|.*\.blip\.tv\/file\/.*|video\.google\.com\/videoplay\?.*|.*revver\.com\/video\/.*|video\.yahoo\.com\/watch\/.*\/.*|video\.yahoo\.com\/network\/.*|.*viddler\.com\/explore\/.*\/videos\/.*|liveleak\.com\/view\?.*|www\.liveleak\.com\/view\?.*|animoto\.com\/play\/.*|dotsub\.com\/view\/.*|www\.overstream\.net\/view\.php\?oid=.*|www\.livestream\.com\/.*|www\.worldstarhiphop\.com\/videos\/video.*\.php\?v=.*|worldstarhiphop\.com\/videos\/video.*\.php\?v=.*|teachertube\.com\/viewVideo\.php.*|www\.teachertube\.com\/viewVideo\.php.*|www1\.teachertube\.com\/viewVideo\.php.*|www2\.teachertube\.com\/viewVideo\.php.*|bambuser\.com\/v\/.*|bambuser\.com\/channel\/.*|bambuser\.com\/channel\/.*\/broadcast\/.*|www\.schooltube\.com\/video\/.*\/.*|bigthink\.com\/ideas\/.*|bigthink\.com\/series\/.*|sendables\.jibjab\.com\/view\/.*|sendables\.jibjab\.com\/originals\/.*|www\.xtranormal\.com\/watch\/.*|.*yfrog\..*\/.*|tweetphoto\.com\/.*|www\.flickr\.com\/photos\/.*|flic\.kr\/.*|.*twitpic\.com\/.*|.*imgur\.com\/.*|.*\.posterous\.com\/.*|post\.ly\/.*|twitgoo\.com\/.*|i.*\.photobucket\.com\/albums\/.*|s.*\.photobucket\.com\/albums\/.*|phodroid\.com\/.*\/.*\/.*|www\.mobypicture\.com\/user\/.*\/view\/.*|moby\.to\/.*|xkcd\.com\/.*|www\.xkcd\.com\/.*|imgs\.xkcd\.com\/.*|www\.asofterworld\.com\/index\.php\?id=.*|www\.asofterworld\.com\/.*\.jpg|asofterworld\.com\/.*\.jpg|www\.qwantz\.com\/index\.php\?comic=.*|23hq\.com\/.*\/photo\/.*|www\.23hq\.com\/.*\/photo\/.*|.*dribbble\.com\/shots\/.*|drbl\.in\/.*|.*\.smugmug\.com\/.*|.*\.smugmug\.com\/.*#.*|emberapp\.com\/.*\/images\/.*|emberapp\.com\/.*\/images\/.*\/sizes\/.*|emberapp\.com\/.*\/collections\/.*\/.*|emberapp\.com\/.*\/categories\/.*\/.*\/.*|embr\.it\/.*|picasaweb\.google\.com.*\/.*\/.*#.*|picasaweb\.google\.com.*\/lh\/photo\/.*|picasaweb\.google\.com.*\/.*\/.*|dailybooth\.com\/.*\/.*|brizzly\.com\/pic\/.*|pics\.brizzly\.com\/.*\.jpg|img\.ly\/.*|www\.tinypic\.com\/view\.php.*|tinypic\.com\/view\.php.*|www\.tinypic\.com\/player\.php.*|tinypic\.com\/player\.php.*|www\.tinypic\.com\/r\/.*\/.*|tinypic\.com\/r\/.*\/.*|.*\.tinypic\.com\/.*\.jpg|.*\.tinypic\.com\/.*\.png|meadd\.com\/.*\/.*|meadd\.com\/.*|.*\.deviantart\.com\/art\/.*|.*\.deviantart\.com\/gallery\/.*|.*\.deviantart\.com\/#\/.*|fav\.me\/.*|.*\.deviantart\.com|.*\.deviantart\.com\/gallery|.*\.deviantart\.com\/.*\/.*\.jpg|.*\.deviantart\.com\/.*\/.*\.gif|.*\.deviantart\.net\/.*\/.*\.jpg|.*\.deviantart\.net\/.*\/.*\.gif|plixi\.com\/p\/.*|plixi\.com\/profile\/home\/.*|plixi\.com\/.*|www\.fotopedia\.com\/.*\/.*|fotopedia\.com\/.*\/.*|photozou\.jp\/photo\/show\/.*\/.*|photozou\.jp\/photo\/photo_only\/.*\/.*|instagr\.am\/p\/.*|skitch\.com\/.*\/.*\/.*|img\.skitch\.com\/.*|share\.ovi\.com\/media\/.*\/.*|www\.questionablecontent\.net\/|questionablecontent\.net\/|www\.questionablecontent\.net\/view\.php.*|questionablecontent\.net\/view\.php.*|questionablecontent\.net\/comics\/.*\.png|www\.questionablecontent\.net\/comics\/.*\.png|picplz\.com\/user\/.*\/pic\/.*\/|twitrpix\.com\/.*|.*\.twitrpix\.com\/.*|www\.someecards\.com\/.*\/.*|someecards\.com\/.*\/.*|some\.ly\/.*|www\.some\.ly\/.*|www\.whitehouse\.gov\/photos-and-video\/video\/.*|www\.whitehouse\.gov\/video\/.*|wh\.gov\/photos-and-video\/video\/.*|wh\.gov\/video\/.*|www\.hulu\.com\/watch.*|www\.hulu\.com\/w\/.*|hulu\.com\/watch.*|hulu\.com\/w\/.*|.*crackle\.com\/c\/.*|www\.fancast\.com\/.*\/videos|www\.funnyordie\.com\/videos\/.*|www\.funnyordie\.com\/m\/.*|funnyordie\.com\/videos\/.*|funnyordie\.com\/m\/.*|www\.vimeo\.com\/groups\/.*\/videos\/.*|www\.vimeo\.com\/.*|vimeo\.com\/m\/#\/featured\/.*|vimeo\.com\/groups\/.*\/videos\/.*|vimeo\.com\/.*|vimeo\.com\/m\/#\/featured\/.*|www\.ted\.com\/talks\/.*\.html.*|www\.ted\.com\/talks\/lang\/.*\/.*\.html.*|www\.ted\.com\/index\.php\/talks\/.*\.html.*|www\.ted\.com\/index\.php\/talks\/lang\/.*\/.*\.html.*|.*nfb\.ca\/film\/.*|www\.thedailyshow\.com\/watch\/.*|www\.thedailyshow\.com\/full-episodes\/.*|www\.thedailyshow\.com\/collection\/.*\/.*\/.*|movies\.yahoo\.com\/movie\/.*\/video\/.*|movies\.yahoo\.com\/movie\/.*\/trailer|movies\.yahoo\.com\/movie\/.*\/video|www\.colbertnation\.com\/the-colbert-report-collections\/.*|www\.colbertnation\.com\/full-episodes\/.*|www\.colbertnation\.com\/the-colbert-report-videos\/.*|www\.comedycentral\.com\/videos\/index\.jhtml\?.*|www\.theonion\.com\/video\/.*|theonion\.com\/video\/.*|wordpress\.tv\/.*\/.*\/.*\/.*\/|www\.traileraddict\.com\/trailer\/.*|www\.traileraddict\.com\/clip\/.*|www\.traileraddict\.com\/poster\/.*|www\.escapistmagazine\.com\/videos\/.*|www\.trailerspy\.com\/trailer\/.*\/.*|www\.trailerspy\.com\/trailer\/.*|www\.trailerspy\.com\/view_video\.php.*|www\.atom\.com\/.*\/.*\/|fora\.tv\/.*\/.*\/.*\/.*|www\.spike\.com\/video\/.*|www\.gametrailers\.com\/video\/.*|gametrailers\.com\/video\/.*|www\.koldcast\.tv\/video\/.*|www\.koldcast\.tv\/#video:.*|techcrunch\.tv\/watch.*|techcrunch\.tv\/.*\/watch.*|mixergy\.com\/.*|video\.pbs\.org\/video\/.*|www\.zapiks\.com\/.*|tv\.digg\.com\/.*|www\.trutv\.com\/video\/.*|www\.nzonscreen\.com\/title\/.*|nzonscreen\.com\/title\/.*|www\.godtube\.com\/featured\/video\/.*|godtube\.com\/featured\/video\/.*|www\.godtube\.com\/watch\/.*|godtube\.com\/watch\/.*|www\.tangle\.com\/view_video.*|mediamatters\.org\/mmtv\/.*|www\.clikthrough\.com\/theater\/video\/.*|soundcloud\.com\/.*|soundcloud\.com\/.*\/.*|soundcloud\.com\/.*\/sets\/.*|soundcloud\.com\/groups\/.*|www\.last\.fm\/music\/.*|www\.last\.fm\/music\/+videos\/.*|www\.last\.fm\/music\/+images\/.*|www\.last\.fm\/music\/.*\/_\/.*|www\.last\.fm\/music\/.*\/.*|www\.mixcloud\.com\/.*\/.*\/|www\.radionomy\.com\/.*\/radio\/.*|radionomy\.com\/.*\/radio\/.*|www\.entertonement\.com\/clips\/.*|www\.rdio\.com\/#\/artist\/.*\/album\/.*|www\.rdio\.com\/artist\/.*\/album\/.*|www\.zero-inch\.com\/.*|.*\.bandcamp\.com\/|.*\.bandcamp\.com\/track\/.*|.*\.bandcamp\.com\/album\/.*|freemusicarchive\.org\/music\/.*|www\.freemusicarchive\.org\/music\/.*|freemusicarchive\.org\/curator\/.*|www\.freemusicarchive\.org\/curator\/.*|www\.npr\.org\/.*\/.*\/.*\/.*\/.*|www\.npr\.org\/.*\/.*\/.*\/.*\/.*\/.*|www\.npr\.org\/.*\/.*\/.*\/.*\/.*\/.*\/.*|www\.npr\.org\/templates\/story\/story\.php.*|espn\.go\.com\/video\/clip.*|espn\.go\.com\/.*\/story.*|abcnews\.com\/.*\/video\/.*|abcnews\.com\/video\/playerIndex.*|washingtonpost\.com\/wp-dyn\/.*\/video\/.*\/.*\/.*\/.*|www\.washingtonpost\.com\/wp-dyn\/.*\/video\/.*\/.*\/.*\/.*|www\.boston\.com\/video.*|boston\.com\/video.*|www\.facebook\.com\/photo\.php.*|www\.facebook\.com\/video\/video\.php.*|www\.facebook\.com\/v\/.*|cnbc\.com\/id\/.*\?.*video.*|www\.cnbc\.com\/id\/.*\?.*video.*|cnbc\.com\/id\/.*\/play\/1\/video\/.*|www\.cnbc\.com\/id\/.*\/play\/1\/video\/.*|cbsnews\.com\/video\/watch\/.*|www\.google\.com\/buzz\/.*\/.*\/.*|www\.google\.com\/buzz\/.*|www\.google\.com\/profiles\/.*|google\.com\/buzz\/.*\/.*\/.*|google\.com\/buzz\/.*|google\.com\/profiles\/.*|www\.cnn\.com\/video\/.*|edition\.cnn\.com\/video\/.*|money\.cnn\.com\/video\/.*|today\.msnbc\.msn\.com\/id\/.*\/vp\/.*|www\.msnbc\.msn\.com\/id\/.*\/vp\/.*|www\.msnbc\.msn\.com\/id\/.*\/ns\/.*|today\.msnbc\.msn\.com\/id\/.*\/ns\/.*|multimedia\.foxsports\.com\/m\/video\/.*\/.*|msn\.foxsports\.com\/video.*|www\.globalpost\.com\/video\/.*|www\.globalpost\.com\/dispatch\/.*|.*amazon\..*\/gp\/product\/.*|.*amazon\..*\/.*\/dp\/.*|.*amazon\..*\/dp\/.*|.*amazon\..*\/o\/ASIN\/.*|.*amazon\..*\/gp\/offer-listing\/.*|.*amazon\..*\/.*\/ASIN\/.*|.*amazon\..*\/gp\/product\/images\/.*|www\.amzn\.com\/.*|amzn\.com\/.*|www\.shopstyle\.com\/browse.*|www\.shopstyle\.com\/action\/apiVisitRetailer.*|www\.shopstyle\.com\/action\/viewLook.*|gist\.github\.com\/.*|twitter\.com\/.*\/status\/.*|twitter\.com\/.*\/statuses\/.*|mobile\.twitter\.com\/.*\/status\/.*|mobile\.twitter\.com\/.*\/statuses\/.*|www\.crunchbase\.com\/.*\/.*|crunchbase\.com\/.*\/.*|www\.slideshare\.net\/.*\/.*|www\.slideshare\.net\/mobile\/.*\/.*|.*\.scribd\.com\/doc\/.*|screenr\.com\/.*|polldaddy\.com\/community\/poll\/.*|polldaddy\.com\/poll\/.*|answers\.polldaddy\.com\/poll\/.*|www\.5min\.com\/Video\/.*|www\.howcast\.com\/videos\/.*|www\.screencast\.com\/.*\/media\/.*|screencast\.com\/.*\/media\/.*|www\.screencast\.com\/t\/.*|screencast\.com\/t\/.*|issuu\.com\/.*\/docs\/.*|www\.kickstarter\.com\/projects\/.*\/.*|www\.scrapblog\.com\/viewer\/viewer\.aspx.*|ping\.fm\/p\/.*|chart\.ly\/.*|maps\.google\.com\/maps\?.*|maps\.google\.com\/\?.*|maps\.google\.com\/maps\/ms\?.*|.*\.craigslist\.org\/.*\/.*|my\.opera\.com\/.*\/albums\/show\.dml\?id=.*|my\.opera\.com\/.*\/albums\/showpic\.dml\?album=.*&picture=.*|tumblr\.com\/.*|.*\.tumblr\.com\/post\/.*|www\.polleverywhere\.com\/polls\/.*|www\.polleverywhere\.com\/multiple_choice_polls\/.*|www\.polleverywhere\.com\/free_text_polls\/.*|www\.quantcast\.com\/wd:.*|www\.quantcast\.com\/.*|siteanalytics\.compete\.com\/.*|statsheet\.com\/statplot\/charts\/.*\/.*\/.*\/.*|statsheet\.com\/statplot\/charts\/e\/.*|statsheet\.com\/.*\/teams\/.*\/.*|statsheet\.com\/tools\/chartlets\?chart=.*|.*\.status\.net\/notice\/.*|identi\.ca\/notice\/.*|brainbird\.net\/notice\/.*|shitmydadsays\.com\/notice\/.*|www\.studivz\.net\/Profile\/.*|www\.studivz\.net\/l\/.*|www\.studivz\.net\/Groups\/Overview\/.*|www\.studivz\.net\/Gadgets\/Info\/.*|www\.studivz\.net\/Gadgets\/Install\/.*|www\.studivz\.net\/.*|www\.meinvz\.net\/Profile\/.*|www\.meinvz\.net\/l\/.*|www\.meinvz\.net\/Groups\/Overview\/.*|www\.meinvz\.net\/Gadgets\/Info\/.*|www\.meinvz\.net\/Gadgets\/Install\/.*|www\.meinvz\.net\/.*|www\.schuelervz\.net\/Profile\/.*|www\.schuelervz\.net\/l\/.*|www\.schuelervz\.net\/Groups\/Overview\/.*|www\.schuelervz\.net\/Gadgets\/Info\/.*|www\.schuelervz\.net\/Gadgets\/Install\/.*|www\.schuelervz\.net\/.*|myloc\.me\/.*|pastebin\.com\/.*|pastie\.org\/.*|www\.pastie\.org\/.*|redux\.com\/stream\/item\/.*\/.*|redux\.com\/f\/.*\/.*|www\.redux\.com\/stream\/item\/.*\/.*|www\.redux\.com\/f\/.*\/.*|cl\.ly\/.*|cl\.ly\/.*\/content|speakerdeck\.com\/u\/.*\/p\/.*|www\.kiva\.org\/lend\/.*|www\.timetoast\.com\/timelines\/.*|storify\.com\/.*\/.*|.*meetup\.com\/.*|meetu\.ps\/.*|www\.dailymile\.com\/people\/.*\/entries\/.*|.*\.kinomap\.com\/.*|www\.metacdn\.com\/api\/users\/.*\/content\/.*|www\.metacdn\.com\/api\/users\/.*\/media\/.*|prezi\.com\/.*\/.*)/i 41 | 42 | $.embedly = $.embedly || {} 43 | if ( $.embedly.version ) { return; } 44 | 45 | $.embedly.version = "2.0.0"; 46 | 47 | $.extend({ 48 | embedly: function(urls, options, callback){ 49 | var elems = []; 50 | var defaults = { 51 | maxWidth: null, // force a maxWidth on all returned media 52 | maxHeight: null, // force a maxHeight on all returned media 53 | wmode: 'opaque', // for flash elements set a wmode 54 | method: 'replace', // embed handling option for standard callback 55 | addImageStyles: true, // add style="" attribute to images for maxWidth and maxHeight 56 | wrapElement: 'div', // standard wrapper around all returned embeds 57 | className: 'embed', // class on the wrapper element 58 | urlRe: null, // custom regex function 59 | key: null, // a pro.embed.ly key 60 | elems: [], // array to hold nodes 61 | success: null // default callback 62 | }; 63 | var settings; 64 | if (typeof options != "undefined") 65 | settings = $.extend(defaults, options); 66 | else 67 | settings = defaults; 68 | if (settings.urlRe == null) settings.urlRe = window.embedlyURLre 69 | if (typeof urls == "string") urls = new Array(urls); 70 | if (typeof callback != "undefined"){ settings.success = callback; } 71 | if (settings.success == null) { 72 | settings.success = function(oembed, dict){ 73 | var _a, elem = $(dict.node); 74 | if (! (oembed) ) { return null; } 75 | if ((_a = settings.method) === 'replace') { return elem.replaceWith(oembed.code); } 76 | else if (_a === 'after') { return elem.after(oembed.code); } 77 | else if (_a === 'afterParent') { return elem.parent().after(oembed.code); } 78 | } 79 | } 80 | var urlValid = function(url){ 81 | return (url.match(settings.urlRe) !== null); 82 | } 83 | 84 | var getParams = function(urls){ 85 | var _p = "urls="+urls; 86 | if (settings.maxWidth != null) {_p += '&maxwidth='+ settings.maxWidth;} 87 | else if (typeof dimensions != "undefined") { _p += '&maxwidth='+ dimensions.width;} 88 | if (settings.maxHeight != null) {_p += '&maxheight=' +settings.maxHeight;} 89 | _p += '&wmode='+ settings.wmode; 90 | if (typeof settings.key == "string") _p += "&key="+settings.key; 91 | return _p; 92 | } 93 | var getUrl = function(){ 94 | return typeof settings.key == "string" ? "http://pro.embed.ly/1/oembed" : "http://api.embed.ly/1/oembed"; 95 | } 96 | 97 | var processEmbed = function(oembed, dict) { 98 | var _a, code, style, title; 99 | if ((_a = oembed.type) === 'photo') { 100 | title = oembed.title || ''; 101 | style = []; 102 | if (settings.addImageStyles) { 103 | if (settings.maxWidth) { 104 | units = typeof settings.maxHeight == "number" ? 'px' : ''; 105 | style.push("max-width: " + (settings.maxWidth)+units); 106 | } 107 | if (settings.maxHeight) { 108 | units = typeof settings.maxHeight == "number" ? 'px' : ''; 109 | style.push("max-height: " + (settings.maxHeight)+units); 110 | } 111 | } 112 | style = style.join(';'); 113 | code = "" + title + ""; 114 | } else if (_a === 'video') { 115 | code = oembed.html; 116 | } else if (_a === 'rich') { 117 | code = oembed.html; 118 | } else { 119 | title = oembed.title || dict.url; 120 | thumb = oembed.thumbnail_url ? '' : ''; 121 | description = oembed.description; 122 | provider = oembed.provider_name ? ''+oembed.provider_name+' - ' : ''; 123 | code = thumb+"" + title + ""; 124 | code += provider; 125 | code += description; 126 | } 127 | if (settings.wrapElement) { 128 | code = '<' + settings.wrapElement+ ' class="'+settings.className+'">' + code + ''; 129 | } 130 | oembed.code = code; 131 | // for DOM elements we add the oembed object as a data field to that element and trigger a custom event called oembed 132 | // with the custom event, developers can do any number of custom interactions with the data that is returned. 133 | if (typeof dict.node != "undefined") { $(dict.node).data('oembed', oembed).trigger('embedly-oembed', [oembed]); } 134 | return settings.success(oembed, dict); 135 | } 136 | 137 | var processBatch = function(batch){ 138 | var data, embed, urls, dimensions; 139 | urls = $.map(batch, 140 | function(e, i) { 141 | if (i == 0) { 142 | if ( e.node !== null){ 143 | node = $(e.node); 144 | dimensions = { 145 | "width": node.parent().width(), 146 | "height": node.parent().height() 147 | }; 148 | } 149 | } 150 | return encodeURIComponent(e.url); 151 | }).join(','); 152 | $.ajax({ 153 | url: getUrl(), 154 | dataType: 'jsonp', 155 | data: getParams(urls), 156 | success: function(data) { 157 | return $.each(data, 158 | function(index, elem) { 159 | return elem.type != 'error' ? processEmbed(elem, batch[index]) : null; 160 | }); 161 | } 162 | }); 163 | } 164 | 165 | $.each(urls, function(i, v){ 166 | node = typeof settings.elems !== "undefined" ? settings.elems[i] : null; 167 | if(typeof node != "undefined" && !(urlValid(v) || settings.key)){ 168 | $(node).data('oembed', false); 169 | } 170 | return v && (urlValid(v) || settings.key) ? elems.push({'url':v, 'node':node }) : null; 171 | }); 172 | var _a = []; 173 | var _b = elems.length; 174 | for (i = 0; (0 <= _b ? i < _b: i > _b); i += 20) { 175 | _a = _a.concat(processBatch(elems.slice(i, i + 20))); 176 | } 177 | if(settings.elems){ 178 | return settings.elems; 179 | } else 180 | return this; 181 | } 182 | }); 183 | 184 | $.fn.embedly = function(options, callback){ 185 | var urlRe = window.embedlyURLre 186 | var settings = typeof options != "undefined" ? options : {}; 187 | // callback is a legacy option, we should be moving towards including a success method in the options 188 | if (typeof callback != "undefined") {options.success = callback; } 189 | //settings.elems = this; 190 | var urls = new Array(); 191 | var nodes = new Array(); 192 | this.each(function(){ 193 | if (typeof $(this).attr('href') != "undefined"){ 194 | if (urlRe.test($(this).attr('href')) ) { 195 | urls.push($(this).attr('href')); 196 | nodes.push($(this)); 197 | } 198 | } else { 199 | $(this).find('a').each(function(){ 200 | if( typeof $(this).attr('href') != "undefined" && urlRe.test($(this).attr('href')) ){ 201 | urls.push($(this).attr('href')); 202 | nodes.push($(this)); 203 | } 204 | }); 205 | } 206 | settings.elems = nodes; 207 | }); 208 | var elems = $.embedly(urls, settings); 209 | return this; 210 | }; 211 | })(jQuery); 212 | -------------------------------------------------------------------------------- /qunit.js: -------------------------------------------------------------------------------- 1 | /* 2 | * QUnit - A JavaScript Unit Testing Framework 3 | * 4 | * http://docs.jquery.com/QUnit 5 | * 6 | * Copyright (c) 2009 John Resig, Jörn Zaefferer 7 | * Dual licensed under the MIT (MIT-LICENSE.txt) 8 | * and GPL (GPL-LICENSE.txt) licenses. 9 | */ 10 | 11 | (function(window) { 12 | 13 | var QUnit = { 14 | 15 | // call on start of module test to prepend name to all tests 16 | module: function(name, testEnvironment) { 17 | config.currentModule = name; 18 | 19 | synchronize(function() { 20 | if ( config.currentModule ) { 21 | QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all ); 22 | } 23 | 24 | config.currentModule = name; 25 | config.moduleTestEnvironment = testEnvironment; 26 | config.moduleStats = { all: 0, bad: 0 }; 27 | 28 | QUnit.moduleStart( name, testEnvironment ); 29 | }); 30 | }, 31 | 32 | asyncTest: function(testName, expected, callback) { 33 | if ( arguments.length === 2 ) { 34 | callback = expected; 35 | expected = 0; 36 | } 37 | 38 | QUnit.test(testName, expected, callback, true); 39 | }, 40 | 41 | test: function(testName, expected, callback, async) { 42 | var name = '' + testName + '', testEnvironment, testEnvironmentArg; 43 | 44 | if ( arguments.length === 2 ) { 45 | callback = expected; 46 | expected = null; 47 | } 48 | // is 2nd argument a testEnvironment? 49 | if ( expected && typeof expected === 'object') { 50 | testEnvironmentArg = expected; 51 | expected = null; 52 | } 53 | 54 | if ( config.currentModule ) { 55 | name = '' + config.currentModule + ": " + name; 56 | } 57 | 58 | if ( !validTest(config.currentModule + ": " + testName) ) { 59 | return; 60 | } 61 | 62 | synchronize(function() { 63 | 64 | testEnvironment = extend({ 65 | setup: function() {}, 66 | teardown: function() {} 67 | }, config.moduleTestEnvironment); 68 | if (testEnvironmentArg) { 69 | extend(testEnvironment,testEnvironmentArg); 70 | } 71 | 72 | QUnit.testStart( testName, testEnvironment ); 73 | 74 | // allow utility functions to access the current test environment 75 | QUnit.current_testEnvironment = testEnvironment; 76 | 77 | config.assertions = []; 78 | config.expected = expected; 79 | 80 | var tests = id("qunit-tests"); 81 | if (tests) { 82 | var b = document.createElement("strong"); 83 | b.innerHTML = "Running " + name; 84 | var li = document.createElement("li"); 85 | li.appendChild( b ); 86 | li.id = "current-test-output"; 87 | tests.appendChild( li ) 88 | } 89 | 90 | try { 91 | if ( !config.pollution ) { 92 | saveGlobal(); 93 | } 94 | 95 | testEnvironment.setup.call(testEnvironment); 96 | } catch(e) { 97 | QUnit.ok( false, "Setup failed on " + name + ": " + e.message ); 98 | } 99 | }); 100 | 101 | synchronize(function() { 102 | if ( async ) { 103 | QUnit.stop(); 104 | } 105 | 106 | try { 107 | callback.call(testEnvironment); 108 | } catch(e) { 109 | fail("Test " + name + " died, exception and test follows", e, callback); 110 | QUnit.ok( false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message ); 111 | // else next test will carry the responsibility 112 | saveGlobal(); 113 | 114 | // Restart the tests if they're blocking 115 | if ( config.blocking ) { 116 | start(); 117 | } 118 | } 119 | }); 120 | 121 | synchronize(function() { 122 | try { 123 | checkPollution(); 124 | testEnvironment.teardown.call(testEnvironment); 125 | } catch(e) { 126 | QUnit.ok( false, "Teardown failed on " + name + ": " + e.message ); 127 | } 128 | }); 129 | 130 | synchronize(function() { 131 | try { 132 | QUnit.reset(); 133 | } catch(e) { 134 | fail("reset() failed, following Test " + name + ", exception and reset fn follows", e, reset); 135 | } 136 | 137 | if ( config.expected && config.expected != config.assertions.length ) { 138 | QUnit.ok( false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run" ); 139 | } 140 | 141 | var good = 0, bad = 0, 142 | tests = id("qunit-tests"); 143 | 144 | config.stats.all += config.assertions.length; 145 | config.moduleStats.all += config.assertions.length; 146 | 147 | if ( tests ) { 148 | var ol = document.createElement("ol"); 149 | 150 | for ( var i = 0; i < config.assertions.length; i++ ) { 151 | var assertion = config.assertions[i]; 152 | 153 | var li = document.createElement("li"); 154 | li.className = assertion.result ? "pass" : "fail"; 155 | li.innerHTML = assertion.message || "(no message)"; 156 | ol.appendChild( li ); 157 | 158 | if ( assertion.result ) { 159 | good++; 160 | } else { 161 | bad++; 162 | config.stats.bad++; 163 | config.moduleStats.bad++; 164 | } 165 | } 166 | if (bad == 0) { 167 | ol.style.display = "none"; 168 | } 169 | 170 | var b = document.createElement("strong"); 171 | b.innerHTML = name + " (" + bad + ", " + good + ", " + config.assertions.length + ")"; 172 | 173 | addEvent(b, "click", function() { 174 | var next = b.nextSibling, display = next.style.display; 175 | next.style.display = display === "none" ? "block" : "none"; 176 | }); 177 | 178 | addEvent(b, "dblclick", function(e) { 179 | var target = e && e.target ? e.target : window.event.srcElement; 180 | if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) { 181 | target = target.parentNode; 182 | } 183 | if ( window.location && target.nodeName.toLowerCase() === "strong" ) { 184 | window.location.search = "?" + encodeURIComponent(getText([target]).replace(/\(.+\)$/, "").replace(/(^\s*|\s*$)/g, "")); 185 | } 186 | }); 187 | 188 | var li = id("current-test-output"); 189 | li.id = ""; 190 | li.className = bad ? "fail" : "pass"; 191 | li.removeChild( li.firstChild ); 192 | li.appendChild( b ); 193 | li.appendChild( ol ); 194 | 195 | if ( bad ) { 196 | var toolbar = id("qunit-testrunner-toolbar"); 197 | if ( toolbar ) { 198 | toolbar.style.display = "block"; 199 | id("qunit-filter-pass").disabled = null; 200 | id("qunit-filter-missing").disabled = null; 201 | } 202 | } 203 | 204 | } else { 205 | for ( var i = 0; i < config.assertions.length; i++ ) { 206 | if ( !config.assertions[i].result ) { 207 | bad++; 208 | config.stats.bad++; 209 | config.moduleStats.bad++; 210 | } 211 | } 212 | } 213 | 214 | QUnit.testDone( testName, bad, config.assertions.length ); 215 | 216 | if ( !window.setTimeout && !config.queue.length ) { 217 | done(); 218 | } 219 | }); 220 | 221 | synchronize( done ); 222 | }, 223 | 224 | /** 225 | * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. 226 | */ 227 | expect: function(asserts) { 228 | config.expected = asserts; 229 | }, 230 | 231 | /** 232 | * Asserts true. 233 | * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); 234 | */ 235 | ok: function(a, msg) { 236 | msg = escapeHtml(msg); 237 | QUnit.log(a, msg); 238 | 239 | config.assertions.push({ 240 | result: !!a, 241 | message: msg 242 | }); 243 | }, 244 | 245 | /** 246 | * Checks that the first two arguments are equal, with an optional message. 247 | * Prints out both actual and expected values. 248 | * 249 | * Prefered to ok( actual == expected, message ) 250 | * 251 | * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." ); 252 | * 253 | * @param Object actual 254 | * @param Object expected 255 | * @param String message (optional) 256 | */ 257 | equal: function(actual, expected, message) { 258 | push(expected == actual, actual, expected, message); 259 | }, 260 | 261 | notEqual: function(actual, expected, message) { 262 | push(expected != actual, actual, expected, message); 263 | }, 264 | 265 | deepEqual: function(actual, expected, message) { 266 | push(QUnit.equiv(actual, expected), actual, expected, message); 267 | }, 268 | 269 | notDeepEqual: function(actual, expected, message) { 270 | push(!QUnit.equiv(actual, expected), actual, expected, message); 271 | }, 272 | 273 | strictEqual: function(actual, expected, message) { 274 | push(expected === actual, actual, expected, message); 275 | }, 276 | 277 | notStrictEqual: function(actual, expected, message) { 278 | push(expected !== actual, actual, expected, message); 279 | }, 280 | 281 | raises: function(fn, message) { 282 | try { 283 | fn(); 284 | ok( false, message ); 285 | } 286 | catch (e) { 287 | ok( true, message ); 288 | } 289 | }, 290 | 291 | start: function() { 292 | // A slight delay, to avoid any current callbacks 293 | if ( window.setTimeout ) { 294 | window.setTimeout(function() { 295 | if ( config.timeout ) { 296 | clearTimeout(config.timeout); 297 | } 298 | 299 | config.blocking = false; 300 | process(); 301 | }, 13); 302 | } else { 303 | config.blocking = false; 304 | process(); 305 | } 306 | }, 307 | 308 | stop: function(timeout) { 309 | config.blocking = true; 310 | 311 | if ( timeout && window.setTimeout ) { 312 | config.timeout = window.setTimeout(function() { 313 | QUnit.ok( false, "Test timed out" ); 314 | QUnit.start(); 315 | }, timeout); 316 | } 317 | } 318 | 319 | }; 320 | 321 | // Backwards compatibility, deprecated 322 | QUnit.equals = QUnit.equal; 323 | QUnit.same = QUnit.deepEqual; 324 | 325 | // Maintain internal state 326 | var config = { 327 | // The queue of tests to run 328 | queue: [], 329 | 330 | // block until document ready 331 | blocking: true 332 | }; 333 | 334 | // Load paramaters 335 | (function() { 336 | var location = window.location || { search: "", protocol: "file:" }, 337 | GETParams = location.search.slice(1).split('&'); 338 | 339 | for ( var i = 0; i < GETParams.length; i++ ) { 340 | GETParams[i] = decodeURIComponent( GETParams[i] ); 341 | if ( GETParams[i] === "noglobals" ) { 342 | GETParams.splice( i, 1 ); 343 | i--; 344 | config.noglobals = true; 345 | } else if ( GETParams[i].search('=') > -1 ) { 346 | GETParams.splice( i, 1 ); 347 | i--; 348 | } 349 | } 350 | 351 | // restrict modules/tests by get parameters 352 | config.filters = GETParams; 353 | 354 | // Figure out if we're running the tests from a server or not 355 | QUnit.isLocal = !!(location.protocol === 'file:'); 356 | })(); 357 | 358 | // Expose the API as global variables, unless an 'exports' 359 | // object exists, in that case we assume we're in CommonJS 360 | if ( typeof exports === "undefined" || typeof require === "undefined" ) { 361 | extend(window, QUnit); 362 | window.QUnit = QUnit; 363 | } else { 364 | extend(exports, QUnit); 365 | exports.QUnit = QUnit; 366 | } 367 | 368 | // define these after exposing globals to keep them in these QUnit namespace only 369 | extend(QUnit, { 370 | config: config, 371 | 372 | // Initialize the configuration options 373 | init: function() { 374 | extend(config, { 375 | stats: { all: 0, bad: 0 }, 376 | moduleStats: { all: 0, bad: 0 }, 377 | started: +new Date, 378 | updateRate: 1000, 379 | blocking: false, 380 | autostart: true, 381 | autorun: false, 382 | assertions: [], 383 | filters: [], 384 | queue: [] 385 | }); 386 | 387 | var tests = id("qunit-tests"), 388 | banner = id("qunit-banner"), 389 | result = id("qunit-testresult"); 390 | 391 | if ( tests ) { 392 | tests.innerHTML = ""; 393 | } 394 | 395 | if ( banner ) { 396 | banner.className = ""; 397 | } 398 | 399 | if ( result ) { 400 | result.parentNode.removeChild( result ); 401 | } 402 | }, 403 | 404 | /** 405 | * Resets the test setup. Useful for tests that modify the DOM. 406 | */ 407 | reset: function() { 408 | if ( window.jQuery ) { 409 | jQuery("#main, #qunit-fixture").html( config.fixture ); 410 | } 411 | }, 412 | 413 | /** 414 | * Trigger an event on an element. 415 | * 416 | * @example triggerEvent( document.body, "click" ); 417 | * 418 | * @param DOMElement elem 419 | * @param String type 420 | */ 421 | triggerEvent: function( elem, type, event ) { 422 | if ( document.createEvent ) { 423 | event = document.createEvent("MouseEvents"); 424 | event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, 425 | 0, 0, 0, 0, 0, false, false, false, false, 0, null); 426 | elem.dispatchEvent( event ); 427 | 428 | } else if ( elem.fireEvent ) { 429 | elem.fireEvent("on"+type); 430 | } 431 | }, 432 | 433 | // Safe object type checking 434 | is: function( type, obj ) { 435 | return QUnit.objectType( obj ) == type; 436 | }, 437 | 438 | objectType: function( obj ) { 439 | if (typeof obj === "undefined") { 440 | return "undefined"; 441 | 442 | // consider: typeof null === object 443 | } 444 | if (obj === null) { 445 | return "null"; 446 | } 447 | 448 | var type = Object.prototype.toString.call( obj ) 449 | .match(/^\[object\s(.*)\]$/)[1] || ''; 450 | 451 | switch (type) { 452 | case 'Number': 453 | if (isNaN(obj)) { 454 | return "nan"; 455 | } else { 456 | return "number"; 457 | } 458 | case 'String': 459 | case 'Boolean': 460 | case 'Array': 461 | case 'Date': 462 | case 'RegExp': 463 | case 'Function': 464 | return type.toLowerCase(); 465 | } 466 | if (typeof obj === "object") { 467 | return "object"; 468 | } 469 | return undefined; 470 | }, 471 | 472 | // Logging callbacks 473 | begin: function() {}, 474 | done: function(failures, total) {}, 475 | log: function(result, message) {}, 476 | testStart: function(name, testEnvironment) {}, 477 | testDone: function(name, failures, total) {}, 478 | moduleStart: function(name, testEnvironment) {}, 479 | moduleDone: function(name, failures, total) {} 480 | }); 481 | 482 | if ( typeof document === "undefined" || document.readyState === "complete" ) { 483 | config.autorun = true; 484 | } 485 | 486 | addEvent(window, "load", function() { 487 | QUnit.begin(); 488 | 489 | // Initialize the config, saving the execution queue 490 | var oldconfig = extend({}, config); 491 | QUnit.init(); 492 | extend(config, oldconfig); 493 | 494 | config.blocking = false; 495 | 496 | var userAgent = id("qunit-userAgent"); 497 | if ( userAgent ) { 498 | userAgent.innerHTML = navigator.userAgent; 499 | } 500 | var banner = id("qunit-header"); 501 | if ( banner ) { 502 | banner.innerHTML = '' + banner.innerHTML + ''; 503 | } 504 | 505 | var toolbar = id("qunit-testrunner-toolbar"); 506 | if ( toolbar ) { 507 | toolbar.style.display = "none"; 508 | 509 | var filter = document.createElement("input"); 510 | filter.type = "checkbox"; 511 | filter.id = "qunit-filter-pass"; 512 | filter.disabled = true; 513 | addEvent( filter, "click", function() { 514 | var li = document.getElementsByTagName("li"); 515 | for ( var i = 0; i < li.length; i++ ) { 516 | if ( li[i].className.indexOf("pass") > -1 ) { 517 | li[i].style.display = filter.checked ? "none" : ""; 518 | } 519 | } 520 | }); 521 | toolbar.appendChild( filter ); 522 | 523 | var label = document.createElement("label"); 524 | label.setAttribute("for", "qunit-filter-pass"); 525 | label.innerHTML = "Hide passed tests"; 526 | toolbar.appendChild( label ); 527 | 528 | var missing = document.createElement("input"); 529 | missing.type = "checkbox"; 530 | missing.id = "qunit-filter-missing"; 531 | missing.disabled = true; 532 | addEvent( missing, "click", function() { 533 | var li = document.getElementsByTagName("li"); 534 | for ( var i = 0; i < li.length; i++ ) { 535 | if ( li[i].className.indexOf("fail") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) { 536 | li[i].parentNode.parentNode.style.display = missing.checked ? "none" : "block"; 537 | } 538 | } 539 | }); 540 | toolbar.appendChild( missing ); 541 | 542 | label = document.createElement("label"); 543 | label.setAttribute("for", "qunit-filter-missing"); 544 | label.innerHTML = "Hide missing tests (untested code is broken code)"; 545 | toolbar.appendChild( label ); 546 | } 547 | 548 | var main = id('main') || id('qunit-fixture'); 549 | if ( main ) { 550 | config.fixture = main.innerHTML; 551 | } 552 | 553 | if (config.autostart) { 554 | QUnit.start(); 555 | } 556 | }); 557 | 558 | function done() { 559 | if ( config.doneTimer && window.clearTimeout ) { 560 | window.clearTimeout( config.doneTimer ); 561 | config.doneTimer = null; 562 | } 563 | 564 | if ( config.queue.length ) { 565 | config.doneTimer = window.setTimeout(function(){ 566 | if ( !config.queue.length ) { 567 | done(); 568 | } else { 569 | synchronize( done ); 570 | } 571 | }, 13); 572 | 573 | return; 574 | } 575 | 576 | config.autorun = true; 577 | 578 | // Log the last module results 579 | if ( config.currentModule ) { 580 | QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all ); 581 | } 582 | 583 | var banner = id("qunit-banner"), 584 | tests = id("qunit-tests"), 585 | html = ['Tests completed in ', 586 | +new Date - config.started, ' milliseconds.
', 587 | '', config.stats.all - config.stats.bad, ' tests of ', config.stats.all, ' passed, ', config.stats.bad,' failed.'].join(''); 588 | 589 | if ( banner ) { 590 | banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); 591 | } 592 | 593 | if ( tests ) { 594 | var result = id("qunit-testresult"); 595 | 596 | if ( !result ) { 597 | result = document.createElement("p"); 598 | result.id = "qunit-testresult"; 599 | result.className = "result"; 600 | tests.parentNode.insertBefore( result, tests.nextSibling ); 601 | } 602 | 603 | result.innerHTML = html; 604 | } 605 | 606 | QUnit.done( config.stats.bad, config.stats.all ); 607 | } 608 | 609 | function validTest( name ) { 610 | var i = config.filters.length, 611 | run = false; 612 | 613 | if ( !i ) { 614 | return true; 615 | } 616 | 617 | while ( i-- ) { 618 | var filter = config.filters[i], 619 | not = filter.charAt(0) == '!'; 620 | 621 | if ( not ) { 622 | filter = filter.slice(1); 623 | } 624 | 625 | if ( name.indexOf(filter) !== -1 ) { 626 | return !not; 627 | } 628 | 629 | if ( not ) { 630 | run = true; 631 | } 632 | } 633 | 634 | return run; 635 | } 636 | 637 | function escapeHtml(s) { 638 | s = s === null ? "" : s + ""; 639 | return s.replace(/[\&"<>\\]/g, function(s) { 640 | switch(s) { 641 | case "&": return "&"; 642 | case "\\": return "\\\\"; 643 | case '"': return '\"'; 644 | case "<": return "<"; 645 | case ">": return ">"; 646 | default: return s; 647 | } 648 | }); 649 | } 650 | 651 | function push(result, actual, expected, message) { 652 | message = escapeHtml(message) || (result ? "okay" : "failed"); 653 | message = '' + message + ""; 654 | expected = escapeHtml(QUnit.jsDump.parse(expected)); 655 | actual = escapeHtml(QUnit.jsDump.parse(actual)); 656 | var output = message + ', expected: ' + expected + ''; 657 | if (actual != expected) { 658 | output += ' result: ' + actual + ', diff: ' + QUnit.diff(expected, actual); 659 | } 660 | 661 | // can't use ok, as that would double-escape messages 662 | QUnit.log(result, output); 663 | config.assertions.push({ 664 | result: !!result, 665 | message: output 666 | }); 667 | } 668 | 669 | function synchronize( callback ) { 670 | config.queue.push( callback ); 671 | 672 | if ( config.autorun && !config.blocking ) { 673 | process(); 674 | } 675 | } 676 | 677 | function process() { 678 | var start = (new Date()).getTime(); 679 | 680 | while ( config.queue.length && !config.blocking ) { 681 | if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) { 682 | config.queue.shift()(); 683 | 684 | } else { 685 | setTimeout( process, 13 ); 686 | break; 687 | } 688 | } 689 | } 690 | 691 | function saveGlobal() { 692 | config.pollution = []; 693 | 694 | if ( config.noglobals ) { 695 | for ( var key in window ) { 696 | config.pollution.push( key ); 697 | } 698 | } 699 | } 700 | 701 | function checkPollution( name ) { 702 | var old = config.pollution; 703 | saveGlobal(); 704 | 705 | var newGlobals = diff( old, config.pollution ); 706 | if ( newGlobals.length > 0 ) { 707 | ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); 708 | config.expected++; 709 | } 710 | 711 | var deletedGlobals = diff( config.pollution, old ); 712 | if ( deletedGlobals.length > 0 ) { 713 | ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); 714 | config.expected++; 715 | } 716 | } 717 | 718 | // returns a new Array with the elements that are in a but not in b 719 | function diff( a, b ) { 720 | var result = a.slice(); 721 | for ( var i = 0; i < result.length; i++ ) { 722 | for ( var j = 0; j < b.length; j++ ) { 723 | if ( result[i] === b[j] ) { 724 | result.splice(i, 1); 725 | i--; 726 | break; 727 | } 728 | } 729 | } 730 | return result; 731 | } 732 | 733 | function fail(message, exception, callback) { 734 | if ( typeof console !== "undefined" && console.error && console.warn ) { 735 | console.error(message); 736 | console.error(exception); 737 | console.warn(callback.toString()); 738 | 739 | } else if ( window.opera && opera.postError ) { 740 | opera.postError(message, exception, callback.toString); 741 | } 742 | } 743 | 744 | function extend(a, b) { 745 | for ( var prop in b ) { 746 | a[prop] = b[prop]; 747 | } 748 | 749 | return a; 750 | } 751 | 752 | function addEvent(elem, type, fn) { 753 | if ( elem.addEventListener ) { 754 | elem.addEventListener( type, fn, false ); 755 | } else if ( elem.attachEvent ) { 756 | elem.attachEvent( "on" + type, fn ); 757 | } else { 758 | fn(); 759 | } 760 | } 761 | 762 | function id(name) { 763 | return !!(typeof document !== "undefined" && document && document.getElementById) && 764 | document.getElementById( name ); 765 | } 766 | 767 | // Test for equality any JavaScript type. 768 | // Discussions and reference: http://philrathe.com/articles/equiv 769 | // Test suites: http://philrathe.com/tests/equiv 770 | // Author: Philippe Rathé 771 | QUnit.equiv = function () { 772 | 773 | var innerEquiv; // the real equiv function 774 | var callers = []; // stack to decide between skip/abort functions 775 | var parents = []; // stack to avoiding loops from circular referencing 776 | 777 | // Call the o related callback with the given arguments. 778 | function bindCallbacks(o, callbacks, args) { 779 | var prop = QUnit.objectType(o); 780 | if (prop) { 781 | if (QUnit.objectType(callbacks[prop]) === "function") { 782 | return callbacks[prop].apply(callbacks, args); 783 | } else { 784 | return callbacks[prop]; // or undefined 785 | } 786 | } 787 | } 788 | 789 | var callbacks = function () { 790 | 791 | // for string, boolean, number and null 792 | function useStrictEquality(b, a) { 793 | if (b instanceof a.constructor || a instanceof b.constructor) { 794 | // to catch short annotaion VS 'new' annotation of a declaration 795 | // e.g. var i = 1; 796 | // var j = new Number(1); 797 | return a == b; 798 | } else { 799 | return a === b; 800 | } 801 | } 802 | 803 | return { 804 | "string": useStrictEquality, 805 | "boolean": useStrictEquality, 806 | "number": useStrictEquality, 807 | "null": useStrictEquality, 808 | "undefined": useStrictEquality, 809 | 810 | "nan": function (b) { 811 | return isNaN(b); 812 | }, 813 | 814 | "date": function (b, a) { 815 | return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf(); 816 | }, 817 | 818 | "regexp": function (b, a) { 819 | return QUnit.objectType(b) === "regexp" && 820 | a.source === b.source && // the regex itself 821 | a.global === b.global && // and its modifers (gmi) ... 822 | a.ignoreCase === b.ignoreCase && 823 | a.multiline === b.multiline; 824 | }, 825 | 826 | // - skip when the property is a method of an instance (OOP) 827 | // - abort otherwise, 828 | // initial === would have catch identical references anyway 829 | "function": function () { 830 | var caller = callers[callers.length - 1]; 831 | return caller !== Object && 832 | typeof caller !== "undefined"; 833 | }, 834 | 835 | "array": function (b, a) { 836 | var i, j, loop; 837 | var len; 838 | 839 | // b could be an object literal here 840 | if ( ! (QUnit.objectType(b) === "array")) { 841 | return false; 842 | } 843 | 844 | len = a.length; 845 | if (len !== b.length) { // safe and faster 846 | return false; 847 | } 848 | 849 | //track reference to avoid circular references 850 | parents.push(a); 851 | for (i = 0; i < len; i++) { 852 | loop = false; 853 | for(j=0;j= 0) { 998 | type = "array"; 999 | } else { 1000 | type = typeof obj; 1001 | } 1002 | return type; 1003 | }, 1004 | separator:function() { 1005 | return this.multiline ? this.HTML ? '
' : '\n' : this.HTML ? ' ' : ' '; 1006 | }, 1007 | indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing 1008 | if ( !this.multiline ) 1009 | return ''; 1010 | var chr = this.indentChar; 1011 | if ( this.HTML ) 1012 | chr = chr.replace(/\t/g,' ').replace(/ /g,' '); 1013 | return Array( this._depth_ + (extra||0) ).join(chr); 1014 | }, 1015 | up:function( a ) { 1016 | this._depth_ += a || 1; 1017 | }, 1018 | down:function( a ) { 1019 | this._depth_ -= a || 1; 1020 | }, 1021 | setParser:function( name, parser ) { 1022 | this.parsers[name] = parser; 1023 | }, 1024 | // The next 3 are exposed so you can use them 1025 | quote:quote, 1026 | literal:literal, 1027 | join:join, 1028 | // 1029 | _depth_: 1, 1030 | // This is the list of parsers, to modify them, use jsDump.setParser 1031 | parsers:{ 1032 | window: '[Window]', 1033 | document: '[Document]', 1034 | error:'[ERROR]', //when no parser is found, shouldn't happen 1035 | unknown: '[Unknown]', 1036 | 'null':'null', 1037 | undefined:'undefined', 1038 | 'function':function( fn ) { 1039 | var ret = 'function', 1040 | name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE 1041 | if ( name ) 1042 | ret += ' ' + name; 1043 | ret += '('; 1044 | 1045 | ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join(''); 1046 | return join( ret, this.parse(fn,'functionCode'), '}' ); 1047 | }, 1048 | array: array, 1049 | nodelist: array, 1050 | arguments: array, 1051 | object:function( map ) { 1052 | var ret = [ ]; 1053 | this.up(); 1054 | for ( var key in map ) 1055 | ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) ); 1056 | this.down(); 1057 | return join( '{', ret, '}' ); 1058 | }, 1059 | node:function( node ) { 1060 | var open = this.HTML ? '<' : '<', 1061 | close = this.HTML ? '>' : '>'; 1062 | 1063 | var tag = node.nodeName.toLowerCase(), 1064 | ret = open + tag; 1065 | 1066 | for ( var a in this.DOMAttrs ) { 1067 | var val = node[this.DOMAttrs[a]]; 1068 | if ( val ) 1069 | ret += ' ' + a + '=' + this.parse( val, 'attribute' ); 1070 | } 1071 | return ret + close + open + '/' + tag + close; 1072 | }, 1073 | functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function 1074 | var l = fn.length; 1075 | if ( !l ) return ''; 1076 | 1077 | var args = Array(l); 1078 | while ( l-- ) 1079 | args[l] = String.fromCharCode(97+l);//97 is 'a' 1080 | return ' ' + args.join(', ') + ' '; 1081 | }, 1082 | key:quote, //object calls it internally, the key part of an item in a map 1083 | functionCode:'[code]', //function calls it internally, it's the content of the function 1084 | attribute:quote, //node calls it internally, it's an html attribute value 1085 | string:quote, 1086 | date:quote, 1087 | regexp:literal, //regex 1088 | number:literal, 1089 | 'boolean':literal 1090 | }, 1091 | DOMAttrs:{//attributes to dump from nodes, name=>realName 1092 | id:'id', 1093 | name:'name', 1094 | 'class':'className' 1095 | }, 1096 | HTML:false,//if true, entities are escaped ( <, >, \t, space and \n ) 1097 | indentChar:' ',//indentation unit 1098 | multiline:false //if true, items in a collection, are separated by a \n, else just a space. 1099 | }; 1100 | 1101 | return jsDump; 1102 | })(); 1103 | 1104 | // from Sizzle.js 1105 | function getText( elems ) { 1106 | var ret = "", elem; 1107 | 1108 | for ( var i = 0; elems[i]; i++ ) { 1109 | elem = elems[i]; 1110 | 1111 | // Get the text from text nodes and CDATA nodes 1112 | if ( elem.nodeType === 3 || elem.nodeType === 4 ) { 1113 | ret += elem.nodeValue; 1114 | 1115 | // Traverse everything else, except comment nodes 1116 | } else if ( elem.nodeType !== 8 ) { 1117 | ret += getText( elem.childNodes ); 1118 | } 1119 | } 1120 | 1121 | return ret; 1122 | }; 1123 | 1124 | /* 1125 | * Javascript Diff Algorithm 1126 | * By John Resig (http://ejohn.org/) 1127 | * Modified by Chu Alan "sprite" 1128 | * 1129 | * Released under the MIT license. 1130 | * 1131 | * More Info: 1132 | * http://ejohn.org/projects/javascript-diff-algorithm/ 1133 | * 1134 | * Usage: QUnit.diff(expected, actual) 1135 | * 1136 | * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick brown fox jumped jumps over" 1137 | */ 1138 | QUnit.diff = (function() { 1139 | function diff(o, n){ 1140 | var ns = new Object(); 1141 | var os = new Object(); 1142 | 1143 | for (var i = 0; i < n.length; i++) { 1144 | if (ns[n[i]] == null) 1145 | ns[n[i]] = { 1146 | rows: new Array(), 1147 | o: null 1148 | }; 1149 | ns[n[i]].rows.push(i); 1150 | } 1151 | 1152 | for (var i = 0; i < o.length; i++) { 1153 | if (os[o[i]] == null) 1154 | os[o[i]] = { 1155 | rows: new Array(), 1156 | n: null 1157 | }; 1158 | os[o[i]].rows.push(i); 1159 | } 1160 | 1161 | for (var i in ns) { 1162 | if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) { 1163 | n[ns[i].rows[0]] = { 1164 | text: n[ns[i].rows[0]], 1165 | row: os[i].rows[0] 1166 | }; 1167 | o[os[i].rows[0]] = { 1168 | text: o[os[i].rows[0]], 1169 | row: ns[i].rows[0] 1170 | }; 1171 | } 1172 | } 1173 | 1174 | for (var i = 0; i < n.length - 1; i++) { 1175 | if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null && 1176 | n[i + 1] == o[n[i].row + 1]) { 1177 | n[i + 1] = { 1178 | text: n[i + 1], 1179 | row: n[i].row + 1 1180 | }; 1181 | o[n[i].row + 1] = { 1182 | text: o[n[i].row + 1], 1183 | row: i + 1 1184 | }; 1185 | } 1186 | } 1187 | 1188 | for (var i = n.length - 1; i > 0; i--) { 1189 | if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null && 1190 | n[i - 1] == o[n[i].row - 1]) { 1191 | n[i - 1] = { 1192 | text: n[i - 1], 1193 | row: n[i].row - 1 1194 | }; 1195 | o[n[i].row - 1] = { 1196 | text: o[n[i].row - 1], 1197 | row: i - 1 1198 | }; 1199 | } 1200 | } 1201 | 1202 | return { 1203 | o: o, 1204 | n: n 1205 | }; 1206 | } 1207 | 1208 | return function(o, n){ 1209 | o = o.replace(/\s+$/, ''); 1210 | n = n.replace(/\s+$/, ''); 1211 | var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/)); 1212 | 1213 | var str = ""; 1214 | 1215 | var oSpace = o.match(/\s+/g); 1216 | if (oSpace == null) { 1217 | oSpace = [" "]; 1218 | } 1219 | else { 1220 | oSpace.push(" "); 1221 | } 1222 | var nSpace = n.match(/\s+/g); 1223 | if (nSpace == null) { 1224 | nSpace = [" "]; 1225 | } 1226 | else { 1227 | nSpace.push(" "); 1228 | } 1229 | 1230 | if (out.n.length == 0) { 1231 | for (var i = 0; i < out.o.length; i++) { 1232 | str += '' + out.o[i] + oSpace[i] + ""; 1233 | } 1234 | } 1235 | else { 1236 | if (out.n[0].text == null) { 1237 | for (n = 0; n < out.o.length && out.o[n].text == null; n++) { 1238 | str += '' + out.o[n] + oSpace[n] + ""; 1239 | } 1240 | } 1241 | 1242 | for (var i = 0; i < out.n.length; i++) { 1243 | if (out.n[i].text == null) { 1244 | str += '' + out.n[i] + nSpace[i] + ""; 1245 | } 1246 | else { 1247 | var pre = ""; 1248 | 1249 | for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) { 1250 | pre += '' + out.o[n] + oSpace[n] + ""; 1251 | } 1252 | str += " " + out.n[i].text + nSpace[i] + pre; 1253 | } 1254 | } 1255 | } 1256 | 1257 | return str; 1258 | } 1259 | })(); 1260 | 1261 | })(this); 1262 | --------------------------------------------------------------------------------